package plp.orientadaObjetos2.declaracao;

import plp.expressions2.memory.VariavelJaDeclaradaException;
import plp.expressions2.memory.VariavelNaoDeclaradaException;
import plp.imperative1.util.Lista;
import plp.orientadaObjetos1.declaracao.classe.DecClasse;
import plp.orientadaObjetos1.excecao.declaracao.ClasseJaDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.ClasseNaoDeclaradaException;
import plp.orientadaObjetos1.excecao.declaracao.ProcedimentoJaDeclaradoException;
import plp.orientadaObjetos1.excecao.declaracao.ProcedimentoNaoDeclaradoException;
import plp.orientadaObjetos2.declaracao.classe.DecClasseNativa;
import plp.orientadaObjetos2.declaracao.classe.DecClasseSimplesOO2;
import plp.orientadaObjetos2.memoria.AmbienteCompilacaoOO2;
import plp.orientadaObjetos2.memoria.AmbienteExecucaoOO2;
import plp.orientadaObjetos2.memoria.FuncaoJaDeclaradaException;


public class ListaDeclaracaoOO extends Lista<DecClasse> {
	/**
	 * Construtor.
	 */
	public ListaDeclaracaoOO() {
	}

	/**
	 * Construtor.
	 * 
	 * @param DeclaracaoOO
	 *            DeclaracaoOO que compoe a tail.
	 */
	public ListaDeclaracaoOO(DecClasse decOO) {
		super(decOO, new ListaDeclaracaoOO());
	}

	/**
	 * Construtor.
	 * 
	 * @param declaracaoOO
	 *            Primeira declaracao da tail.
	 * @param listaDeclaracaoOO
	 *            Restante da tail de declaracoes.
	 */
	public ListaDeclaracaoOO(DecClasse decOO, ListaDeclaracaoOO lista) {
		super(decOO, lista);
	}

	public AmbienteExecucaoOO2 elabora(AmbienteExecucaoOO2 ambiente)
			throws VariavelJaDeclaradaException, VariavelNaoDeclaradaException,
			ClasseJaDeclaradaException, ClasseNaoDeclaradaException,
			ProcedimentoNaoDeclaradoException, ProcedimentoJaDeclaradoException, ConstrutorNaoDeclaradoException {
		
		if (length() == 1) {
			elaboraClasse(ambiente);
		} else {
			elaboraClasse(ambiente);
			((ListaDeclaracaoOO)getTail()).elabora(ambiente);
		}
		
		return ambiente;
	}

	private void elaboraClasse(AmbienteExecucaoOO2 ambiente)
			throws ClasseJaDeclaradaException, ClasseNaoDeclaradaException,
			ConstrutorNaoDeclaradoException, VariavelNaoDeclaradaException, VariavelJaDeclaradaException, ProcedimentoJaDeclaradoException, ProcedimentoNaoDeclaradoException {
		DecClasse classe =  getHead();
		if(classe instanceof DecClasseSimplesOO2){
			((DecClasseSimplesOO2)classe).elabora(ambiente);
		} else if(classe instanceof DecClasseNativa){
			((DecClasseNativa)classe).elabora(ambiente);
		} else {
			classe.elabora(ambiente);
		}
	}

	public boolean checaTipo(AmbienteCompilacaoOO2 ambiente)
			throws VariavelJaDeclaradaException, VariavelNaoDeclaradaException,
			ClasseJaDeclaradaException, ClasseNaoDeclaradaException,
			ProcedimentoNaoDeclaradoException, ProcedimentoJaDeclaradoException, ConstrutorNaoDeclaradoException, FuncaoJaDeclaradaException {
		
		boolean ret = false;
		if (length() == 1) {
			ret = checaTipoClasse(ambiente);
		} else {
			ret = checaTipoClasse(ambiente);
			if (ret)
				ret = ((ListaDeclaracaoOO)getTail()).checaTipo(ambiente);
		}
		
		return ret;
	}

	private boolean checaTipoClasse(AmbienteCompilacaoOO2 ambiente)
			throws ClasseJaDeclaradaException, ClasseNaoDeclaradaException,
			ProcedimentoNaoDeclaradoException,
			ProcedimentoJaDeclaradoException, ConstrutorNaoDeclaradoException,
			FuncaoJaDeclaradaException {
		boolean ret;
		DecClasse classe = getHead();
		if(classe instanceof DecClasseSimplesOO2){
			ret = ((DecClasseSimplesOO2)classe).checaTipo(ambiente);
		} else if(classe instanceof DecClasseNativa){
			ret = ((DecClasseNativa)classe).checaTipo(ambiente);
		} else {
			ret = classe.checaTipo(ambiente);
		}
		return ret;
	}
}
